const path = require('path')

const { sortPlugins } = require('@vue/cli-shared-utils')

const PackageManager = require('./ProjectPackageManager')

const { loadPlugins } = require('../utils/pkg')
const writeFileTree = require('../utils/writeFileTree')
const sortObject = require('../utils/sortObject')
const { compile, extract, fileDisplay } = require('../templates/')

module.exports = class Generator {
  constructor(
    context,
    { pkg = {}, plugins = [], afterInvokeCbs = [], afterAnyInvokeCbs = [], files = {}, invoking = false }
  ) {
    this.context = context
    this.plugins = sortPlugins(plugins)
    this.originalPkg = pkg
    this.pkg = Object.assign({}, pkg)
    this.pm = new PackageManager({ context })

    this.imports = {}
    this.rootOptions = {}
    this.afterInvokeCbs = afterInvokeCbs
    this.afterAnyInvokeCbs = afterAnyInvokeCbs
    this.invoking = invoking

    this.files = files

    this.rootOptions = {}
    // load all the other plugins
  }

  async generate({ rootOptions, sortPackageJson = true }) {
    this.rootOptions = rootOptions
    await loadPlugins(this.pkg, rootOptions)
    // set package.json
    if (sortPackageJson) {
      this.sortPkg()
    }
    // extract configs from package.json into dedicated files.
    await this.extractConfigFiles()
    // wait for file resolve
    await this.resolveFiles()

    this.files['package.json'] = JSON.stringify(this.pkg, null, 2) + '\n'

    await writeFileTree(this.context, this.files)
  }

  async extractConfigFiles() {
    if (this.rootOptions.type === 'vue') {
      let configTemplate = ['.editorConfig', 'jsconfig.json', 'vue.config.js']

      if (this.rootOptions.features.includes('babel')) {
        configTemplate.push('babel.config.js')
      }

      if (this.rootOptions.features.includes('linter')) {
        configTemplate = configTemplate.concat(['.eslintrc.js', '.eslintignore', '.prettierrc', '.prettierignore'])
      }

      if (this.rootOptions.features.includes('githook')) {
        configTemplate.push('lint-staged.config.js')
        configTemplate = configTemplate.concat(['lint-staged.config.js', 'commitlint.config.js'])
      }

      await extract.call(this, configTemplate)
    }

    this.files['.gitignore'] = [
      '.DS_Store',
      'node_modules',
      'dist',
      '/tests/e2e/',
      '/tests/e2e/',
      '',
      '.env.local',
      '.env.*.local',
      'npm-debug.log*',
      'yarn-debug.log*',
      'yarn-error.log*',
      '',
      '# Editor directories and files',
      '.idea',
      '.vscode',
      '.history',
      '*.suo',
      '*.ntvs*',
      '*.njsproj',
      '*.sln',
      '*.sw?',
      `/${this.rootOptions.name}/`,
      'package-lock.json',
      'pnpm-lock.yaml',
      '*.zip'
    ].join('\n')
  }

  async resolveFiles() {
    const { type, vueVersion } = this.rootOptions
    if (this.rootOptions.type === 'vue') {
      const temPath = path.resolve(__dirname, `../templates/${type}${vueVersion}`)
      let files = []
      await fileDisplay(temPath, files)
      let fileKeys = files.map((fileTree) => fileTree.join('/'))

      for await (const filePath of fileKeys) {
        const result = await compile(`${type}${vueVersion}/${filePath}`, this.rootOptions)
        this.files[filePath] = result
      }
    }
  }

  sortPkg() {
    // ensure package.json keys has readable order
    this.pkg.dependencies = sortObject(this.pkg.dependencies)
    this.pkg.devDependencies = sortObject(this.pkg.devDependencies)
    this.pkg.scripts = sortObject(this.pkg.scripts, [
      'start',
      'serve',
      'build',
      'lint',
      'lint-staged',
      'prettier',
      'prepare',
      'test:unit',
      'test:e2e'
    ])
    this.pkg = sortObject(this.pkg, [
      'name',
      'version',
      'private',
      'description',
      'author',
      'scripts',
      'main',
      'module',
      'browser',
      'jsDelivr',
      'unpkg',
      'files',
      'dependencies',
      'devDependencies',
      'peerDependencies',
      'vue',
      'babel',
      'gitHooks',
      'eslintConfig',
      'config',
      'prettier',
      'postcss',
      'browserslist',
      'jest'
    ])
  }
}
